/*
 * CDROM.C Version 1.01 (C)1996 HUDSON SOFT
 * MIX 再生サンプルプログラムCD-ROMエミュレーション関連(CDROM.C)
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <io.h>
#include <dos.h>
#include <fcntl.h>
#include <time.h>
#include "mixplay.h"

#define	ESC	0x1b

enum {
    F_GETID = 0, F_GETSTAT, F_IOREQ, F_IOREQ_BRK, F_RESET
};

enum {
    C_INIT = 0, C_BUS_RST, C_ENB_SEL, C_DIS_SEL, C_SND_MES,
    C_SND_STAT, C_RCV_MES, C_RCV_CMD, C_BUS_SNS, C_CMD_CMP,
    C_SND_DAT,  C_SND_CMP, C_SND_BLK, C_RCV_DAT, C_RCV_CMP,
    C_RCV_BLK,  C_DIS_CON, C_TMR_STA, C_CHK_TMR, C_BUS_PHS,
    C_GET_SEL
};

enum {
    P_DATA_OUT = 0x00, P_DATA_IN, P_COMMAND, P_RSV1, P_RSV2, P_MES_OUT,
    P_MES_IN
};

#define	ST_WAIT		0x00
#define	ST_OK		0x01
#define	ST_ABORT	0x02
#define	ST_ERR		0x04
#define	ST_PHASE	0x08
#define	ST_ATN		0x10
#define	ST_REQERR	0x80
#define	ST_ADPERR	0x81
#define	ST_DEVERR	0x82

#define	S_TG_SEL_ATN	0x20
#define	S_TG_SEL	0x40
#define	S_TG_RESET	0x80

#define	TRGT_BUSY	0x8
#define	TRGT_CM		0x4
#define	TRGT_CC		0x2
#define	TRGT_GOOD	0x0

#define	MSG_CMD_CMPL	0x00
#define	MSG_ABORT	0x06
#define	MSG_REJECT	0x07
#define	MSG_DEV_RESET	0x0c

/* group 0 */
#define TEST_UNIT_READY	0x00
#define REQUEST_SENSE	0x03
#define NO_OPERATION	0x0d
#define START_STOP_UNIT	0x1b
/* group 1 */
#define READ_EXTENDED	0x28
/* group 2 */
#define READ_TOC	0x43
/* group 5 */
/* group 6 */
#define EJECT		0xdc

/* SENSE KEY */
#define NO_SENSE 0
#define ILLEGAL_REQUEST 5
#define UNIT_ATTENTION 6
/* SUB ERROR */
#define VIOD_SENSE_DATA 0x00

#define INVALID_COMMAND 0x20
#define INVALID_PARAMETER 0x22
#define END_OF_VOLUME 0x25
#define INVALID_REQUEST_IN_CDB 0x27
#define UNIT_RESET 0x29

#define	REC_MAX	16
#define	MAX_BUF (REC_MAX * 2048)

static unsigned char read_buf[REC_MAX][2048];
static int tgfx_flag = FALSE;
static void (far *asti_entry)(strb far *buf);
static int test_unit_flag = FALSE;
static int eml_end_flag = FALSE;
static int cdb_len;
static unsigned char cdb_buf[12];
static unsigned char mes_buf[20];
static unsigned char trgt_stat;
static unsigned char trgt_rmes;
static unsigned char trgt_smes;
static unsigned char trgt_snskey;
static unsigned char trgt_suberr;
static unsigned char host_id;
static strb trbuf;

/* CD-ROM エミュレータデバイスドライバを呼び出します。
 *
 * 入力
 * なし
 *
 * 出力
 * なし
 */
void asti_call(void)
{
    asm sti
    asm push si
    asm push bx
    asm lea si,trbuf
    asm push ds
    asm push si
    asm lea bx,ds:asti_entry
    asm call dword ptr ds:[bx]
    asm add sp,4
    asm pop bx
    asm pop si
}

/* CD-ROMエミュレータのセレクションを有効にします。
 *
 * 入力
 * なし
 *
 * 出力
 * なし
 */
void tgfx_open(void)
{
    if (!tgfx_flag) {
	trbuf.code = F_IOREQ;
	trbuf.host_no = 0;
	trbuf.cmd = C_ENB_SEL;
	asti_call();
	tgfx_flag = TRUE;
    }
}

/* CD-ROMエミュレータの初期化を行います。
 *
 * CD-ROMエミュレータが使用できない場合はエラーを表示して終了します。
 *
 * 入力
 * なし
 *
 * 出力
 * なし
 */
void tgfx_init(void)
{
    int tgfx_handle;
    if (_dos_open("TGMSCSI$",O_RDONLY,&tgfx_handle) != 0) {
	error("TGFX.SYSが組み込まれていません");
    }
    if (my_ioctl(tgfx_handle,2,&asti_entry,4)!=4) {
	_dos_close(tgfx_handle);
	error("TGFX.SYSの接続に失敗しました");
    }
    _dos_close(tgfx_handle);
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_INIT;
    asti_call();
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_DIS_CON;
    asti_call();
    test_unit_flag = FALSE;
    tgfx_open();
}

/* セレクションされた際の情報を得ます。
 *
 * 入力
 * int              *msgp  セレクション時に受信したメッセージを返すポインタ
 *
 * 出力
 * int		    ??     セレクションしたホストの ID
 */
int tgfx_sel(int *msgp)
{
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_GET_SEL;
    asti_call();
    *msgp = trbuf.data[1];
    return(trbuf.data[0]);
}

/* SCSIデバイスドライバへのリクエスト実行の終了を待ちます。
 *
 * 入力
 * なし
 *
 * 出力
 * int		           送出ステータス
 */
int tgfx_wait(void)
{
    while (1) {
	if (trbuf.stat != ST_WAIT) break;
	trbuf.code = F_GETSTAT;
	trbuf.stat = 0;
	trbuf.host_no = 0;
	asti_call();
    }
    return(trbuf.stat);
}

/* SCSIバスのフェーズの状態を調べます。
 *
 * 入力
 * なし
 *
 * 出力
 * int		    P_???? フェーズの状態
 */
int tgfx_phase(void)
{
    int phs;
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_BUS_PHS;
    asti_call();
    phs = trbuf.data[0];
    tgfx_wait();
    return(phs);
}

/* SCSIバスにデータを送出します。（バイト単位）
 *
 * 入力
 * void		    *datap データのポインタ
 * int		    len    データのバイト数
 *
 * 出力
 * int		           送出ステータス
 */
int tgfx_send_data_byte(void far *datap,int len)
{
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_SND_DAT;
    *(int *)&trbuf.data[0] = len;
    *(unsigned int *)&trbuf.data[2] = FP_OFF(datap);
    *(unsigned int *)&trbuf.data[4] = FP_SEG(datap);
    asti_call();
    return(tgfx_wait());
}

/* SCSIバスにデータを送出します。（ブロック単位）
 *
 * 入力
 * void		    *datap データのポインタ
 * int		    len    データのブロック数
 *
 * 出力
 * int		           送出ステータス
 */
int tgfx_send_data_block(void far *datap,int len)
{
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_SND_BLK;
    *(int *)&trbuf.data[0] = len;
    *(unsigned int *)&trbuf.data[2] = FP_OFF(datap);
    *(unsigned int *)&trbuf.data[4] = FP_SEG(datap);
    asti_call();
    return(tgfx_wait());
}

/* SCSIコマンドを受信します。
 *
 * 受信したコマンドは cdb_buf[] に格納されます。
 *
 * 入力
 * なし
 *
 * 出力
 * なし
 */
void tgfx_getcmd(void)
{
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_RCV_CMD;
    *(unsigned int *)&trbuf.data[2] = FP_OFF((void far *)cdb_buf);
    *(unsigned int *)&trbuf.data[4] = FP_SEG((void far *)cdb_buf);
    asti_call();
    tgfx_wait();

    switch (cdb_buf[0]) {
    case TEST_UNIT_READY:
	cdb_len = 6;
	break;
    case EJECT:			/* このサンプルプログラムは、このコマンドを */
	cdb_len = 10;		/* 動作の終了を通知する目的で使用しています */
	break;
    case START_STOP_UNIT:	/* このサンプルプログラムは、このコマンドを */
	cdb_len = 6;		/* 動作の終了を通知する目的で使用しています */
	break;
    case REQUEST_SENSE:
	cdb_len = 6;
	break;
    case READ_TOC:
	cdb_len = 10;
	break;
    case READ_EXTENDED:
	cdb_len = 10;
	break;
    default:
	cdb_len = 1;		/* 不正なコマンドは長さ不明 */
	break;
    }
}

/* SCSIメッセージを受信します。
 *
 * 入力
 * なし
 *
 * 出力
 * int		    MSG_?? メッセージ
 */
int tgfx_getmes(void)
{
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_RCV_MES;
    *(unsigned int *)&trbuf.data[2] = FP_OFF((void far *)&mes_buf);
    *(unsigned int *)&trbuf.data[4] = FP_SEG((void far *)&mes_buf);
    asti_call();
    dprintf("MO :%02x\n",(int)mes_buf[0]);
    return(mes_buf[0]);
}

/* SCSIメッセージを送信します。
 *
 * 入力
 * int              msg    送信メッセージ
 *
 * 出力
 * なし
 */
void tgfx_sndmes(int mes)
{
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_SND_MES;
    trbuf.data[0] = mes;
    asti_call();
}

/* SCSIコマンドを終了します。
 *
 * コマンドが正常に終了している場合は、実行結果のステータス trgt_stat と、実行
 * 後のメッセージ trgt_smes を送出します。
 *
 * 入力
 * なし
 *
 * 出力
 * なし
 */
void tgfx_endcmd(void)
{
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_CMD_CMP;
    trbuf.data[0] = trgt_stat;
    trbuf.data[1] = trgt_smes;
    asti_call();
    tgfx_wait();
    dprintf("STA:%02x\nMI :%02x\n",(int)trgt_stat,(int)trgt_smes);
    trbuf.code = F_IOREQ;
    trbuf.host_no = 0;
    trbuf.cmd = C_ENB_SEL;
    asti_call();
    tgfx_wait();
}

/* 最終トラック番号を求めます。
 *
 * 入力
 * なし
 *
 * 出力
 * int		    ??     最終トラック番号
 */
int get_max_track(void)
{
    int trk;
    filelist far *p;
    trk = 0;
    for (p = file_list_top;p;) {
	trk = p->track;
	p = p->next;
    }
    return(trk);
}

/* レコード位置を sect 型構造体にセットします。（形式１）
 *
 * 入力
 * unsigned long    sec;   レコード位置
 * int              msf;   情報形式（0 = rec , 0 以外 = msf）
 * sect             *dp;   情報をセットするポインタ
 *
 * 出力
 * なし
 */
void set_sec_data1(unsigned long sec,int msf,sect *dp)
{
    if (msf) {
	sec += 150L;
	dp->sec0 = 0;
	dp->sec1 = sec / 4500L;
	dp->sec2 = (sec / 75L) % 60L;
	dp->sec3 = sec % 75L;
    } else {
	dp->sec0 = (sec >> 24) & 0xff;
	dp->sec1 = (sec >> 16) & 0xff;
	dp->sec2 = (sec >>  8) & 0xff;
	dp->sec3 =  sec        & 0xff;
    }
}

/* 指定のレコード番号を含むトラックを探します。
 *
 * 入力
 * unsigned long    sec;   レコード位置
 *
 * 出力
 * int              1〜99  トラック番号
 *		    0xaa   エラー
 */
int get_track_no(unsigned long sec)
{
    filelist far *p;
    p = get_sec_list(sec);
    if (!p) return(0xaa);
    return(p->track);
}

/* レコード位置を sect 型構造体にセットします。（形式２）
 *
 * 入力
 * unsigned long    sec;   レコード位置
 * int              typ;   情報形式（0 = rec , 1 = msf , 2 = track）
 * sect             *dp;   情報をセットするポインタ
 *
 * 出力
 * なし
 */
void set_sec_data2(unsigned long sec,int typ,sect *dp)
{
    switch (typ) {
    case 0:
	dp->sec0 = (sec >> 24) & 0xff;
	dp->sec1 = (sec >> 16) & 0xff;
	dp->sec2 = (sec >>  8) & 0xff;
	dp->sec3 =  sec        & 0xff;
	break;
    case 1:
	sec += 150L;
	dp->sec0 = bin2bcd(sec / 4500L);
	dp->sec1 = bin2bcd((sec / 75L) % 60L);
	dp->sec2 = bin2bcd(sec % 75L);
	dp->sec3 = 0;
	break;
    case 2:
	dp->sec0 = bin2bcd(get_track_no(sec));
	dp->sec1 = 0;
	dp->sec2 = 0;
	dp->sec3 = 0;
	break;
    default:
	dp->sec0 = 0;
	dp->sec1 = 0;
	dp->sec2 = 0;
	dp->sec3 = 0;
	break;
    }
}

/* sect 型構造体をレコード番号に変換します。（形式２）
 *
 * 入力
 * sect             *dp;   変換する構造体のポインタ
 * int              typ;   情報形式（0 = rec , 1 = msf , 2 = track）
 *
 * 出力
 * unsigned long           レコード位置
 */
unsigned long get_sec_data2(sect *dp,int typ)
{
    int trk;
    unsigned long sec,lsec;
    filelist far *p;
    p = get_track_list(get_max_track());
    lsec = p->sec_end + 1;
    switch (typ) {
    case 0:
	sec = (((unsigned long)dp->sec0) << 24) +
	      (((unsigned long)dp->sec1) << 16) +
	      (((unsigned long)dp->sec2) <<  8) +
	        (unsigned long)dp->sec3;
	break;
    case 1:
	sec = ((unsigned long)bcd2bin(dp->sec0) * 60L +
	       (unsigned long)bcd2bin(dp->sec1)) * 75L +
	       (unsigned long)bcd2bin(dp->sec2);
	break;
    case 2:
	if (dp->sec0 == 0xaa) {
	    sec = lsec;
	} else {
	    p = get_track_list(bcd2bin(dp->sec0));
	    if (p) sec = p->sec_top;
	    else sec = lsec;
	}
	break;
    default:
	sec = lsec;
    }
    if (sec > lsec) sec = lsec;
    return(sec);
}

/* 指定トラックの開始位置を sect 型構造体にセットします。
 *
 * 入力
 * int              track; トラック番号（1〜99,0xAA）
 * int              msf;   情報形式（0 = rec , 0 以外 = msf）
 * sect             *dp;   情報をセットするポインタ
 *
 * 出力
 * int		    0      正常終了
 *                  0 以外 トラック番号が不正
 */
int set_start_sec(int track,int msf,sect *dp)
{
    int leadout;
    unsigned long sec;
    filelist far *p;
    if (track == 0xaa) {
	leadout = TRUE;
	track = get_max_track();
    } else leadout = FALSE;
    if ((track < 1) || (track > get_max_track())) return(TRUE);
    p = get_track_list(track);
    if (leadout) sec = p->sec_end + 1;
    else sec = p->sec_top;
    set_sec_data1(sec,msf,dp);
    return(FALSE);
}

/* REQUEST_SENSE コマンドを処理します。
 *
 * 入力
 * なし
 *
 * 出力
 * int		    ST_OK  正常終了
 *                  上記外 エラー
 */
int request_sense(void)
{
    int req_len;
    sense_data sd;
    req_len = cdb_buf[4];
    sd.err_code = 0x70;
    sd.rsv0 = 0;
    sd.sns_key = trgt_snskey;
    set_sec_data2(0L,0,&sd.sec_dat);
    sd.add_len = 0x0a;		/*          bit 76 543 210 */
    sd.dev_id = 0x10;		/* id = 2 /     00|010|000 */
    sd.rsv_sub_err = trgt_suberr;
    sd.rsv1 = 0;
    sd.rsv2 = 0;
    sd.sub_err = trgt_suberr;
    sd.rsv3 = 0;
    sd.rsv4 = 0;
    sd.rsv5 = 0;
    sd.rsv6 = 0;
    sd.rsv7 = 0;
    if (req_len == 0) req_len = 4;
    if (req_len > 18) req_len = 18;
    trgt_stat = TRGT_GOOD;
    trgt_snskey = NO_SENSE;
    trgt_suberr = VIOD_SENSE_DATA;
    return(tgfx_send_data_byte((void far *)&sd,req_len));
}

/* READ_TOC コマンドを処理します。
 *
 * 入力
 * なし
 *
 * 出力
 * int		    ST_OK  正常終了
 *                  上記外 エラー
 */
int read_toc(void)
{
    int req_len,len,slen,trk,strk,etrk,sts;
    toc_head th;
    toc_info ti;
    etrk = get_max_track();
    strk = cdb_buf[6];
    if (set_start_sec(strk,cdb_buf[1] & 0x02,&ti.sec_dat)) {
	trgt_stat = TRGT_CC;
	trgt_snskey = ILLEGAL_REQUEST;
	trgt_suberr = INVALID_REQUEST_IN_CDB;
	return(ST_OK);
    }
    if (strk == 0xaa) len = sizeof(toc_info) + sizeof(toc_head);
    else len = (etrk - strk + 2) * sizeof(toc_info) + sizeof(toc_head);
    req_len = (cdb_buf[7] << 8) + cdb_buf[8];
    th.len0 = ((len - 2) >> 8) & 0xff;
    th.len1 =  (len - 2)       & 0xff;
    th.st_trk = 1;
    th.ed_trk = etrk;
    slen = (req_len > sizeof(toc_head)) ? sizeof(toc_head) : req_len;
    if ((sts = tgfx_send_data_byte((void far *)&th,slen)) != ST_OK) return(sts);
    req_len -= slen;
    ti.rsv0 = 0;
    ti.adr_ctrl = 0x04;
    ti.rsv1 = 1;
    for (trk = strk;req_len;) {
	ti.trk_no = trk;
	set_start_sec(trk,cdb_buf[1] & 0x02,&ti.sec_dat);
	slen = (req_len > sizeof(toc_info)) ? sizeof(toc_info) : req_len;
	if ((sts = tgfx_send_data_byte((void far *)&ti,slen)) != ST_OK)
	    return(sts);
	req_len -= slen;
	if (trk == 0xaa) break;
	trk++;
	if (trk > etrk) trk = 0xaa;
    }
    trgt_stat = TRGT_GOOD;
    trgt_snskey = NO_SENSE;
    trgt_suberr = VIOD_SENSE_DATA;
    return(ST_OK);
}

/* READ_EXTENDED コマンドを処理します。
 *
 * 何かキーを押すと強制中断します。
 *
 * 入力
 * なし
 *
 * 出力
 * int		    ST_OK  正常終了
 *                  上記外 エラー
 */
int read_extended(void)
{
    unsigned long st_sec,ed_sec,sc_len;
    long spos;
    int fhd,len,sts,cnt;
    unsigned int slen;
    filelist far *p;
    char name[256];
    st_sec = get_sec_data2((sect *)&cdb_buf[2],(cdb_buf[9] >> 6) & 3);
    p = get_sec_list(st_sec);
    sc_len = ((unsigned long)cdb_buf[7] << 8) + (unsigned long)cdb_buf[8];
    ed_sec = st_sec + sc_len - 1L;
    if (!p) {
	trgt_stat = TRGT_CC;
	trgt_snskey = ILLEGAL_REQUEST;
	trgt_suberr = END_OF_VOLUME;
	return(ST_OK);
    }
    if (sc_len == 0) {
	trgt_stat = TRGT_GOOD;
	trgt_snskey = NO_SENSE;
	trgt_suberr = VIOD_SENSE_DATA;
	return(ST_OK);
    }
    if (p->sec_end < ed_sec) {
	trgt_stat = TRGT_CC;
	trgt_snskey = ILLEGAL_REQUEST;
	trgt_suberr = INVALID_REQUEST_IN_CDB;
	return(ST_OK);
    }
    _fstrcpy((char far *)name,p->name);
    if (_dos_open(name,O_RDONLY,&fhd) != 0) {
	error("データファイルがオープンできません");
    }
    spos = (st_sec - p->sec_top) << 11;
    my_seek(fhd,spos,SEEK_SET);
    cnt = sc_len / REC_MAX;
    len = sc_len % REC_MAX;
    sts = ST_OK;
    while(cnt) {
	if (_dos_read(fhd,(void far *)read_buf,MAX_BUF,&slen) != 0) {
	    _dos_close(fhd);
	    error("データファイルが読み込みできません");
	}
	if ((sts = tgfx_send_data_block(read_buf,REC_MAX)) != ST_OK) break;
	cnt--;
	if (kbhit()) {
	    if (getch() == ESC) {
		len = 0;		/* 余りの部分の再生をキャンセル */
		break;
	    }
	}
    }
    if ((len != 0) && (sts == ST_OK)) {
	slen = len << 11;
	if (_dos_read(fhd,(void far *)read_buf,slen,&slen) != 0) {
	    _dos_close(fhd);
	    error("データファイルが読み込みできません");
	} else {
	    sts = tgfx_send_data_block(read_buf,len);
	}
    }
    _dos_close(fhd);
    trgt_stat = TRGT_GOOD;
    trgt_snskey = NO_SENSE;
    trgt_suberr = VIOD_SENSE_DATA;
    return(sts);
}

/* SCSIコマンドを処理します。
 *
 * TEST_UNIT_READY , START_STOP_UNIT , REQUEST_SENSE 以外のコマンドは、リセット
 * 直後から TEST_UNIT_READY を行うまでの間はエラーになります。
 *
 * 入力
 * なし
 *
 * 出力
 * int		    ST_OK  正常終了
 *                  上記外 エラー
 */
int exec_scsi_cmd(void)
{
    int sts;
    trgt_stat = TRGT_CC;		/* 取り敢えずエラーにしておく */
    trgt_smes = MSG_CMD_CMPL;
    trgt_rmes = MSG_CMD_CMPL;
    trgt_snskey = UNIT_ATTENTION;	/* 取り敢えずのセンスキー */
    trgt_suberr = UNIT_RESET;		/* 取り敢えずのエラーコード */
    sts = ST_OK;
    switch (cdb_buf[0]) {
    case TEST_UNIT_READY:
	test_unit_flag = TRUE;
	trgt_stat = TRGT_GOOD;
	trgt_snskey = NO_SENSE;
	trgt_suberr = VIOD_SENSE_DATA;
	break;
    case EJECT:			/* このサンプルプログラムは、このコマンドを */
	eml_status = EM_TO;	/* 動作の終了を通知する目的で使用しています */
    case START_STOP_UNIT:	/* このサンプルプログラムは、このコマンドを */
	eml_end_flag = TRUE;	/* 動作の終了を通知する目的で使用しています */
	trgt_stat = TRGT_GOOD;
	trgt_snskey = NO_SENSE;
	trgt_suberr = VIOD_SENSE_DATA;
	break;
    case REQUEST_SENSE:
	sts = request_sense();
	break;
    case READ_TOC:
	if (test_unit_flag) {	/* TEST UNIT READY 後は OK */
	    sts = read_toc();
	}
	break;
    case READ_EXTENDED:
	if (test_unit_flag) {	/* TEST UNIT READY 後は OK */
	    sts = read_extended();
	}
	break;
    default:
	trgt_stat = TRGT_CC;
	trgt_snskey = ILLEGAL_REQUEST;
	trgt_suberr = INVALID_COMMAND;
	break;
    }
    return(sts);
}

/* CD-ROMエミュレータのセレクションを無効にします。
 *
 * 入力
 * なし
 *
 * 出力
 * なし
 */
void tgfx_close(void)
{
    if (tgfx_flag) {
	trbuf.code = F_IOREQ;
	trbuf.host_no = 0;
	trbuf.cmd = C_DIS_CON;
	asti_call();
	trbuf.code = F_IOREQ;
	trbuf.host_no = 0;
	trbuf.cmd = C_DIS_SEL;
	asti_call();
	tgfx_flag = FALSE;
    }
}

/* CD-ROMをエミュレーションします。
 *
 * 入力
 * なし
 *
 * 出力
 * なし
 */
void cdrom_eml(void)
{
    int bus_data,sel_mes,i;
    eml_end_flag = FALSE;
    tgfx_open();
    while (1) {
	if (eml_end_flag) break;
	trbuf.code = F_IOREQ;
	trbuf.host_no = 0;
	trbuf.cmd = C_BUS_SNS;
	asti_call();
	bus_data = trbuf.data[0];
	tgfx_wait();
	switch (bus_data) {
	case S_TG_RESET:
	    test_unit_flag = FALSE;
	    trbuf.code = F_IOREQ;
	    trbuf.host_no = 0;
	    trbuf.cmd = C_ENB_SEL;
	    asti_call();
	    break;
	case S_TG_RESET + S_TG_SEL:
	    test_unit_flag = FALSE;
	case S_TG_SEL:
	case S_TG_SEL_ATN:
	    host_id = tgfx_sel(&sel_mes);
	    if (bus_data == S_TG_SEL_ATN) {
		if ((sel_mes & 0xc0) == 0) {
		    tgfx_close();
		    tgfx_open();
		    break;
		}
	    }
	    for (;;) {
		if (tgfx_phase() != P_MES_OUT) break;
		tgfx_getmes();
		tgfx_sndmes(MSG_REJECT);
	    }
	    tgfx_getcmd();
	    if (debug_flag) {
		dprintf("CMD:");
		for(i = 0;i < cdb_len;i++) {
		    dprintf("%02x ",(int)cdb_buf[i]);
		}
		dprintf("\n");
	    }
	    switch (exec_scsi_cmd()) {
	    case ST_OK:
		tgfx_endcmd();
		break;
	    case ST_ATN:
		trgt_rmes = tgfx_getmes();
		switch (trgt_rmes) {
		case MSG_ABORT:
		case MSG_DEV_RESET:
		    tgfx_close();
		    tgfx_open();
		    break;
		default:
		    break;
		}
		break;
	    }
	    break;
        }
    }
    tgfx_close();
}
